/* 
 * File:   sytem.c
 * Author: Tomás R. González Barroso  
 * Diseño de sistemas Operativos
 * Created on 26 de junio de 2013, 8:46
 */

#include "system.h"
#include "tf.h"
#include "buffer.h"
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>

extern filetable * tabla_ficheros;

//Añadimos un nuevo fichero a la tabla en exclusión mutua.
int s_open(char *filepath,int mode) {
    int i,fd,fdtf;
    
    
    i = tdfu.o_files;
    //Comprobamos si podemos añadir mas ficheros
    if (i == MAXOPEN) {
        perror("Numero maximo de ficheros en TDFU. open\n");
        return -1;
    }   
    // Creamos si es necesario
    if (mode & O_CREAT) {
        printf("Creando fichero");
        int new = open(filepath, O_CREAT, 0644);
        close(new);
        mode = O_RDWR;
    }
    fdtf = tf_open(filepath,mode);
    if (fdtf != -1) {  
        fd = open(filepath,mode);
        //Buscamos primero libre
        i = 0;
        while ((tdfu.files[i].fd != -1 )&& (i< MAXOPEN))
           i++;
        if (i != MAXOPEN) {
           printf("Agregando fichero a TDFU.\n");
           tdfu.files[i].fdreal = fd; 
           tdfu.files[i].fd = fdtf;            
           tdfu.o_files++; 
        }
        else {
            tf_close(fdtf);            
            i = -1;
        }
        
    }       
    return i;    
}

//Inicializamos estructura
void s_init(void) {
    int i;
    
    tdfu.o_files = 0;
    for (i=0;i< MAXOPEN;i++) {
        tdfu.files[i].fd = -1;
        tdfu.files[i].fdreal = -1;
    }    
}

void s_close(int fd) {
    
    int pid;
    
    printf("Eliminando fichero de la tabla TDFU.\n");    
    pid = getpid();    
    //Buscamos buffers marcados con escritura retardada y guardamos a disco       
    bwrite_fclose(pid,tdfu.files[fd].fdreal) ;   
    //Cerramos fichero        
    close(tdfu.files[fd].fdreal);
    //Eliminamos fichero de la tabla de ficheros
    tf_close(tdfu.files[fd].fd);
    //Eliminamos fichero de TDFU
    tdfu.files[fd].fd = -1;
    tdfu.files[fd].fdreal = -1;
    tdfu.o_files--;    
}

//Buscamos el fd asociado al inode
int s_getfd(ino_t inode) {
    int i=0;
    int encontrado = FALSE;
    
    //buscamos inode en tabla de ficheros apuntado por nuestra TDFU.    
    while ((!encontrado) && (i < tdfu.o_files)) {
       if (tabla_ficheros->files[tdfu.files[i].fd].inode == inode) 
           encontrado = TRUE;
       else
           i++;
    }    
    if (!encontrado) 
        i = -1;
    
    return i;        
    
}


//Visualizamos TDFU
void s_print (void) {
    int i;
        
    printf("Nº de ficheros abiertos TDFU => %d \n" ,tdfu.o_files);    
    //Visualizamos atributos de los ficheros
    for (i=0;i < MAXOPEN;i++) {
        if (tdfu.files[i].fd != -1) {
            printf("Fd(%i) => %i ",i,tdfu.files[i].fd);        
            printf("Fd Real => %i ",tdfu.files[i].fdreal);        
            printf("\n");            
        }
    }    
 }

//Read de usuario
ssize_t s_read(int fd, char *buf, size_t nbytes) {

    file *tmp_f;
    size_t pte;
    off_t io;
    int block;
    off_t offset;
    size_t bytes_leer;
    buffer *b;   
    char * src;
    char * dst;
    
    dst = buf;
    
    //localizamos datos fichero abierto en la tabla de ficheros
    tmp_f = &(tabla_ficheros->files[tdfu.files[fd].fd]);

    //Verificamos permisos
    if (tmp_f->permisos & R_OK) {     
        perror("No dispone permisos de lectura.");
        return -1;
    }

    io = tmp_f->io;
    
    if (nbytes > (tmp_f->tam - tmp_f->io))        
       pte = tmp_f->tam - tmp_f->io; //leemos sólo hasta EOF
    else
       pte = nbytes;
   
    //Repetimos leer todos los bytes, o llegar a EOF
    while ((pte > 0) && (io < tmp_f->tam)) {
        //Calculamo el nº de bloque io / BLKSIZE
        block = bmap(io);

        //Obtenemos buffer
        //Suponemos siempre mismo sistema de ficheros fs=0
        printf("bread(0,%d,%d,%d)\n", (int) tmp_f->inode,block,tdfu.files[fd].fdreal);
        b = bread(0, tmp_f->inode, block,tdfu.files[fd].fdreal);

        //Calculamos el desplazamiento en el buffer cargado
        offset = io % BLKSIZE;
        
        src = b->data + offset;
        
        //Leemos hasta final del bloque o hasta pte
        if (pte < BLKSIZE - offset)
            bytes_leer = pte;
        else
            bytes_leer = BLKSIZE - offset;

        //Copiamos datos desde buffer
        memcpy(dst, src, bytes_leer);
        
        //printf("memcpy dst:%s ,src:%s,%d",dst, src, bytes_leer);
       
        b->tam = bytes_leer + offset;
        //Liberamos buffer
        brelse(b);

        pte = pte - bytes_leer;
        dst = dst + bytes_leer;
        io = io + bytes_leer;
    }
    //total leido io_actual - io_ant
    bytes_leer = io - tmp_f->io;

    // Actualizamos io
    tmp_f->io = io;
    
    return bytes_leer;
}

//Write de usuario
ssize_t s_write(int fd, char *buf, size_t nbytes) {

    file *tmp_f;
    size_t pte;
    off_t io;
    int block;
    off_t offset;
    size_t bytes_escr;
    buffer *b;   
    char * src;
    char * dst;
    
    src = buf;
    
    //localizamos datos fichero abierto en la tabla de ficheros
    tmp_f = &(tabla_ficheros->files[tdfu.files[fd].fd]);

    //Verificamos permisos
    if (!(tmp_f->permisos & W_OK)) {     
        perror("No dispone permisos de escritura.");
        return -1;
    }

    io = tmp_f->io;
    
    pte =  nbytes;
    //Repetimos hasta escribir todos los bytes
    while (pte > 0) {
        
        //Calculamo el nº de bloque io / BLKSIZE
        block = bmap(io);        
        //Calculamos el desplazamiento en el buffer cargado
        offset = io % BLKSIZE;
        
      
        //Obtenemos buffer        
        //Suponemos siempre mismo sistema de ficheros fs=0
        if (offset == 0) {  //Si desde el principio no leemos
            printf("getblk(0,%d,%d)\n", (int) tmp_f->inode,block);            
            b = getblk(0, tmp_f->inode, block);
            dst = b->data;            
        }
        else {
            printf("bread(0,%d,%d)\n", (int) tmp_f->inode,block);
            b = bread(0, tmp_f->inode, block,tdfu.files[fd].fdreal);            
            dst = b->data + offset;
        }
        
        
        //escribimos hasta final del bloque o hasta pte
        if (pte < BLKSIZE - offset)
            bytes_escr = pte;
        else
            bytes_escr = BLKSIZE - offset;
        //printf("memcpy %d ",bytes_escr);
        //printf("memcpy src[%s] dst[%s] \n",src,dst);
        //Copiamos datos al buffer
        memcpy(dst, src, bytes_escr);
        b->status_valid = TRUE; //por si entramos por getblk
        b ->tam = offset + bytes_escr;        
        b->fd = tdfu.files[fd].fdreal;
        b->pid = getpid();
        
        //marcamos escritura retardada
        bwrite(b);
        //Liberamos buffer
        brelse(b);

        pte = pte - bytes_escr;
        src = src + bytes_escr;
        io = io + bytes_escr;
    }
    //total leido io_actual - io_ant
    bytes_escr = io - tmp_f->io;

    // Actualizamos io
    tmp_f->io = io;
    
    return bytes_escr;
}

//Lseek de usuario
off_t s_lseek(int fd, off_t offset, int whence) {

    file *tmp_f;

    //localizamos datos fichero abierto en la tabla de ficheros
    tmp_f = &(tabla_ficheros->files[tdfu.files[fd].fd]);

    // position the i/o pointer
    off_t io = 0;

    switch (whence) {

    // offset set to offset bytes
    case SEEK_SET:
        io = offset;
        break;

    // offset set to current location plus offset bytes
    case SEEK_CUR:
        io = tmp_f->io + offset;
        break;

    // offset set size of the file plus offset bytes
    case SEEK_END:
        io = tmp_f ->tam + offset;
        break;

    // Si es otra opcion, nos situamos al final
    default:
        io = tmp_f ->tam;
    }

    //Si fuera de los limites -> acortamos
    if (io < 0 )
        io = 0;
    if (io > tmp_f -> tam) 
        io = tmp_f ->tam;
    
   
    tmp_f->io = io;

    return io;
}